﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.IO;

namespace SharpDownloadSDK
{
    public class DownloadFileEventArgs : EventArgs
    { 
        private String url = String.Empty;

        public String Url
        {
            get { return url; }
            set { url = value; }
        }
        private String savePath = String.Empty;

        public String SavePath
        {
            get { return savePath; }
            set { savePath = value; }
        }
    }
    public class DownloadFileBeginEventArgs : DownloadFileEventArgs
    {
    }
    public class DownloadFileCompletedEventArgs : DownloadFileEventArgs
    {       
        private bool isSuccessful = true;

        public bool IsSuccessful
        {
            get { return isSuccessful; }
            set { isSuccessful = value; }
        }

        private float currentCompletedFilesPercent = 0;

        public float CurrentCompletedFilesPercent
        {
            get { return currentCompletedFilesPercent; }
            set { currentCompletedFilesPercent = value; }
        }        
    }
    public delegate void DownloadFileBegin(Object sender, DownloadFileBeginEventArgs e);
    public delegate void DownloadFileCompleted(object sender, DownloadFileCompletedEventArgs e);

    public abstract class IDownloader
    {
        private Dictionary<String, String> m_urlAndSavePaths = new Dictionary<String, String>();
        private DownloadFileBegin downloadFileBeginEvent = null;
        private DownloadFileCompleted downloadFileCompletedEvent = null;
        protected int m_currentCompletedFilesNum = 0;

        public class DunnyClass
        {
        }
        private object obj = new DunnyClass();

        public Dictionary<String, String> UrlAndSavePaths
        {
            get { return m_urlAndSavePaths; }
            set { m_urlAndSavePaths = value; }
        } 
        public DownloadFileBegin DownloadFileBeginEvent
        {
            get { return downloadFileBeginEvent; }
            set { downloadFileBeginEvent = value; }
        }
        public DownloadFileCompleted DownloadFileCompletedEvent
        {
            get { return downloadFileCompletedEvent; }
            set { downloadFileCompletedEvent = value; }
        }
        public float CurrenCompletedFilesPercent
        {
            get { return (float)m_currentCompletedFilesNum / m_urlAndSavePaths.Count; }
        }
        public int CurrentCompletedFileNum
        {
            get { return m_currentCompletedFilesNum; }
        }

        protected void FireDownloadFileBeginEvent(String url, String savePath)
        {
            DownloadFileBeginEventArgs downloadFileBeginE = new DownloadFileBeginEventArgs();
            downloadFileBeginE.Url = url;
            downloadFileBeginE.SavePath = savePath;

            if (downloadFileBeginEvent != null)
                downloadFileBeginEvent(null, downloadFileBeginE);
        }
        protected void FireDownloadFileCompletedEvent(String url, String savePath, bool isSuccessful, float completedPercent)
        {
            lock (obj)
            {
                System.Threading.Interlocked.Increment(ref m_currentCompletedFilesNum);

                DownloadFileCompletedEventArgs downloadFileCompletedE = new DownloadFileCompletedEventArgs();
                downloadFileCompletedE.Url = url;
                downloadFileCompletedE.SavePath = savePath;
                downloadFileCompletedE.IsSuccessful = isSuccessful;
                downloadFileCompletedE.CurrentCompletedFilesPercent = CurrenCompletedFilesPercent;

                if (DownloadFileCompletedEvent != null)
                    DownloadFileCompletedEvent(null, downloadFileCompletedE);
            }
        }

        public abstract void DownloadFile(String url, String savePath); 
        public virtual void DownloadFiles()
        {
            foreach (KeyValuePair<String, String> keyValue in UrlAndSavePaths)
            {
                DownloadFile(keyValue.Key, keyValue.Value);                
            }
        }

        public abstract void DownloadFileAsync(String url, String savePath);
        public virtual void DownloadFilesAsync()
        {
            foreach (System.Collections.Generic.KeyValuePair<String, String> keyValue in UrlAndSavePaths)
            {
                DownloadFileAsync(keyValue.Key, keyValue.Value);
            }
        }
    }

    public class WebClientDownloader : IDownloader
    {
        public override void DownloadFile(String url, String savePath)
        {
            FireDownloadFileBeginEvent(url, savePath);
            if (FileUtilities.ValidateFile(savePath))
            {
                FireDownloadFileCompletedEvent(url, savePath, true, CurrenCompletedFilesPercent);
                return;
            }

            bool isSuccessful = true;            
            try
            {
                WebClient webClient = new WebClient();     
                webClient.DownloadFile(new Uri(url), savePath);                
            }
            catch (Exception)
            {
                isSuccessful = false;
            }
            FireDownloadFileCompletedEvent(url, savePath, isSuccessful, CurrenCompletedFilesPercent);
        }
        public override void DownloadFiles()
        {
            base.DownloadFiles();
        }

        private void WebClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {           
            bool isSuccessful = e.Error != null ? false : true;
            String url = e.UserState as String;
            String savePath = UrlAndSavePaths[url];

            FireDownloadFileCompletedEvent(url, savePath, isSuccessful, CurrenCompletedFilesPercent);
        }
        public override void DownloadFileAsync(String url, String savePath)
        {
            FireDownloadFileBeginEvent(url, savePath);

            if (FileUtilities.ValidateFile(savePath))
            {
                FireDownloadFileCompletedEvent(url, savePath, true, CurrenCompletedFilesPercent);
                return;
            }

            WebClient webClient = new WebClient();
            webClient.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(WebClient_DownloadFileCompleted);
            webClient.DownloadFileAsync(new Uri(url), savePath, url);               
        }
        public override void DownloadFilesAsync()
        {
            base.DownloadFilesAsync();
        }
    }

    public class HttpWebRequestDownloader : IDownloader
    {
        public override void DownloadFile(String url, String savePath)
        {
            FireDownloadFileBeginEvent(url, savePath);

            if (FileUtilities.ValidateFile(savePath))
            {
                FireDownloadFileCompletedEvent(url, savePath, true, CurrenCompletedFilesPercent);
                return;
            }

            bool isSuccessful = true;

            try
            {
                HttpWebRequest hwr = (HttpWebRequest)HttpWebRequest.Create(url);
                WebResponse wresp = hwr.GetResponse();
                using (Stream stream = wresp.GetResponseStream())
                {
                    int length = (int)wresp.ContentLength;
                    BinaryReader br = new BinaryReader(stream);
                    FileStream fs;
                    fs = File.Create(savePath);
                    fs.Write(br.ReadBytes(length), 0, length);
                    br.Close();
                    fs.Close();
                    stream.Close();
                }
            }
            catch (Exception e)
            {
                isSuccessful = false;
            }

            FireDownloadFileCompletedEvent(url, savePath, isSuccessful, CurrenCompletedFilesPercent);
        }
        public override void DownloadFiles()
        {
            base.DownloadFiles();
        }

        private void AsyncDownLoad(IAsyncResult asyncResult)
        {
            bool isSuccessful = true;
     
            WebRequest request = (WebRequest)asyncResult.AsyncState;
            string url = request.RequestUri.ToString();
            try
            {
                WebResponse wresp = request.EndGetResponse(asyncResult);
                using (Stream stream = wresp.GetResponseStream())
                {
                    int length = (int)wresp.ContentLength;
                    BinaryReader br = new BinaryReader(stream);
                    FileStream fs;
                    String savePath = UrlAndSavePaths[url];
                    fs = File.Create(savePath);
                    fs.Write(br.ReadBytes(length), 0, length);
                    br.Close();
                    fs.Close();
                    stream.Close();
                }
            }
            catch (Exception e)
            {
                isSuccessful = false;
            }

            FireDownloadFileCompletedEvent(url, UrlAndSavePaths[url], isSuccessful, CurrenCompletedFilesPercent);
        }
        public override void DownloadFileAsync(string url, string savePath)
        {
            FireDownloadFileBeginEvent(url, savePath);
            if (FileUtilities.ValidateFile(savePath))
            {
                FireDownloadFileCompletedEvent(url, savePath, true, CurrenCompletedFilesPercent);
                return;
            }

            WebRequest hwr = WebRequest.Create(url);
            hwr.BeginGetResponse(new AsyncCallback(AsyncDownLoad), hwr);
        }
        public override void DownloadFilesAsync()
        {
            base.DownloadFilesAsync();
        }
    }    
}
